/* TESTED WORKING */

#include "includes.h"
#include <stdlib.h>
#include <errno.h>
#include <stdio.h>
#include "queue.h"
#define NO_QUEUES 8
#define BASE_TIMESLICE 10

extern long clock_time;

static struct queue qs[NO_QUEUES];
static int time_slices[NO_QUEUES];
static long next_event_time = -1;

int exp_queue_empty() {
  int i;
  for (i = 0; i < NO_QUEUES; i++)
    if (!queue_empty_p(&qs[i]))
      return 0;
  return 1;
}

void exp_advance_clock(long time_step) {
  /* No action if queue is empty */
  if (next_event_time == -1) return;

  /* If we advance to our current event, next event time = 0 */
  if (time_step >= next_event_time) 
    next_event_time = 0;
  else
    next_event_time -= time_step;
}

void exp_init() {
  int i;
  /* initialize queues */
  for (i = 0; i < NO_QUEUES; i++)
    init_queue(&qs[i]);
  
  /* initialize time slices */
  time_slices[0] = BASE_TIMESLICE;
  for (i = 1; i < NO_QUEUES; i++)
    time_slices[i] = 2*time_slices[i-1];

  /* initialize next clock time */
  next_event_time = -1;
}

void exp_add_to_queue(struct proc_info *proc) {
  long time_used, time_slice;
  int priority_level;
  
  if (proc == NULL) {
    perror("Null process passed to exp_add_to_queue\n");
    exit(-1);
  }

      
  /* Determine next priority level */
  priority_level = proc->priority_level;
  if (priority_level < 0 || priority_level >= NO_QUEUES) {
    perror("Bad priority level passed to exp queue\n");
    exit(-1);
  }
  
  /* Get timing info for last run */
  time_used = proc->last_runtime;
  time_slice = time_slices[priority_level];

  /* Adjust priority */
  if (time_used >= time_slice) {/*used entire time-slice */
    if (priority_level < NO_QUEUES-1)
      proc->priority_level = priority_level+1;
  } else if (time_used < time_slice/2.0) {
    if (priority_level > 0)
      proc->priority_level = priority_level-1;
  }

  /* Reset last runtime */
  proc->last_runtime = 0;
  
  /* Add to appropriate queue */
  if (enqueue(&qs[proc->priority_level], proc)) {
    perror("Ran out of memeory in exp queue\n");
    exit(-1);
  }
}

struct proc_info* exp_get_next(){
  int i;
  struct proc_info *next;

  /* Get the next process */
  for (i = 0; i < NO_QUEUES; i++) {
    if (!queue_empty_p(&qs[i])) {
      next = dequeue(&qs[i]);
      next_event_time = time_slices[i];
      return next;
    }
  }

  /* Get here if the queue is empty */
  next_event_time = -1;
  return (struct proc_info*)NULL;
}

long exp_next_event_time() {
  /* If the scheduler is not waiting to interrupt a process */
  if (next_event_time == -1)
    return -1;
  else
    return next_event_time + clock_time;
}

/* Just here for syntax */
void exp_close() {}

void exp_print_queue() {
  struct queue_node *node;
  int i;

  for (i = 0; i < NO_QUEUES; i++) {
    printf("%d :", i);
    if (!queue_empty_p(&qs[i])) {
      node = qs[i].first;
      while (node != NULL) {
	printf("Node %li", node->data->pid);
	node = node->next;
      }
    }
    printf("\n");
  }
}
